
package models;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.ManyToMany;
import javax.persistence.Transient;
import javax.persistence.TypedQuery;

import models.aaa.Asset;
import models.aaa.Node;
import models.security.Permission;
import models.security.Role;

import org.apache.commons.lang.StringUtils;

import play.data.validation.Constraints;
import play.db.jpa.JPA;

/**
 * Application's user, mostly credentials
 * 
 * @author laurent
 * 
 */
@Entity
public class User extends Node {// implements

                                // be.objectify.deadbolt.models.RoleHolder {

    /**
     * Empty user. I.e. one with default/no permissions.
     * 
     * @author laurent
     * 
     */
    protected static class Empty extends User {

        @Override
        public String getName() {
            return "";
        }

        @Override
        public String getPassword() {
            return UUID.randomUUID().toString();
        }

        @Override
        public List<Permission> getPermissions() {
            return new ArrayList<>();
        }

        @Override
        public List<Role> getRoles() {
            return new ArrayList<>();
        }

        @Override
        public Asset getActor() {
            return null;
        }

        @Override
        public boolean isEmpty() {
            return true;
        }
    }

    public static class Finder {

        public TypedQuery<User> createQuery(final String sql) {
            TypedQuery<User> result = JPA.em().createQuery(sql, User.class);
            return result;
        }

        public TypedQuery<User> query(final String sql, final Object... parameters) {
            TypedQuery<User> result = createQuery(sql);
            for (int i = 0; i < parameters.length; i++) {
                result.setParameter(i + 1, parameters[i]);
            }
            return result;
        }

        public User byId(final String id) {
            if (StringUtils.isEmpty(id)) {
                return User.empty;
            }
            try {
                Long user_id = Long.parseLong(id);
                return byId(user_id);
            } catch (NumberFormatException ex) {
                return User.empty;
            }
        }

        public User byId(final Long id) {
            if (id == null) {
                return User.empty;
            }
            if (id.longValue() == 0) {
                return User.empty;
            }
            return JPA.em().find(User.class, id);
        }

        public User byCredential(final String name, final String password) {
            if (StringUtils.isEmpty(name)) {
                return empty;
            }
            if (StringUtils.isEmpty(password)) {
                return empty;
            }

            TypedQuery<User> query = query("SELECT e FROM User e WHERE e.name = ? AND e.password = ?", name, password);
            List<User> users = query.getResultList();
            for (User user : users) {
                return user;
            }
            return empty;
        }

        public User byName(final String name) {
            if (StringUtils.isEmpty(name)) {
                return empty;
            }

            TypedQuery<User> query = query("SELECT e FROM User e WHERE e.name = ?", name);
            List<User> users = query.getResultList();
            for (User user : users) {
                return user;
            }
            return empty;
        }

    }

    public static final User   empty = new Empty();

    public static final Finder find  = new Finder();

    /**
     * Authenticate a User.
     */
    public static User authenticate(final String name, final String password) {
        return find.byCredential(name, password);
    }

    public User() {
    }

    public User(final String name, final String password) {
        this.name = name;
        this.password = password;
    }

    @Constraints.Required
    private String                 name        = null;

    @Constraints.Required
    private String                 password    = null;

    @ManyToMany(fetch = FetchType.EAGER)
    private final List<Role>       roles       = new ArrayList<>();

    @ManyToMany
    private final List<Permission> permissions = new ArrayList<>();

    @Transient
    private Asset                  actor       = null;

    public String getName() {
        return name;
    }

    public void setName(final String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(final String password) {
        this.password = password;
    }

    // @Override
    public List<Permission> getPermissions() {
        return permissions;
    }

    // @Override
    public List<Role> getRoles() {
        return roles;
    }

    public Asset getActor() {
        if (actor != null) {
            return actor;
        }
        actor = Asset.find.byIdentifier("pid", name);
        return actor;
    }

    public boolean isEmpty() {
        return false;
    }

    @Override
    public String toString() {
        return "User(" + name + ")";
    }

}
